package net.sansi.v3correctserver.task;

import cn.hutool.core.util.IdUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import net.sansi.v3correctserver.constenum.ThreadLocalValues;
import net.sansi.v3correctserver.exception.ServiceException;
import net.sansi.v3correctserver.service.notification.NotificationService;
import net.sansi.v3correctserver.util.Debounce;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.util.Map;

/**
 * Created by Fang
 * create time : 2024/11/1
 * IDE : IntelliJ IDEA
 * <p>
 * 任务
 */

@Getter
@Slf4j
public abstract class V3Task<F extends V3Task<F, P>, P extends V3Task.Progress>
        extends PriorityRunnable
        implements Comparable<PriorityRunnable> {

    private final RequestAttributes requestAttributes;
    private final Map<String, String> headers;
    private final NotificationService notificationService;
    private final Debounce debounce = new Debounce(50);

    /**
     * 构造函数
     *
     * @param priority 优先级
     * @param progress 进度
     */
    public V3Task(int priority, P progress) {
        super(priority);
        this.progress = progress;
        this.progress.setTask(this);
        requestAttributes = RequestContextHolder.getRequestAttributes();
        headers = ThreadLocalValues.instance.requestHeaders.get();
        notificationService = SpringUtil.getBean(NotificationService.class);
    }

    /**
     * 构造函数
     *
     * @param priority 优先级
     * @param callback 完成回调
     * @param progress 进度
     */
    public V3Task(int priority, P progress, Callback<F> callback) {
        this(priority, progress);
        this.finishCallback = callback;
    }


    // 完成回调
    public interface Callback<F> {
        void invoke(F task);
    }

    // 完成回调函数
    @Setter
    protected Callback<F> finishCallback;

    // 任务ID
    protected final String taskId = IdUtil.simpleUUID();


    // 任务状态
    protected String status = Status.WAITING;

    // 时间戳 ms
    protected Long finishTimestamp;

    /**
     * 设置任务状态
     *
     * @param status 状态
     */
    protected void setStatus(String status) {
        this.status = status;
    }

    /**
     * 任务进度
     */
    protected final P progress;

    /**
     * 异常
     */
    protected Exception exception;

    /**
     * 错误信息
     */
    protected String errorMessage;

    /**
     * 运行
     */
    @Override
    public void run() {
        RequestContextHolder.setRequestAttributes(requestAttributes);
        ThreadLocalValues.instance.setRequestHeaders(headers);
        setStatus(Status.RUNNING);
        try {
            runHandler();
            setStatus(Status.FINISH);
        } catch (Exception e) {
            setStatus(Status.FAIL);
            this.exception = e;
            log.info("[taskId {}] {}", taskId, e.getMessage(), e);
            errorMessage = e.getMessage();
        } finally {
            finishTimestamp = System.currentTimeMillis();
            if (finishCallback != null) finishCallback.invoke((F) this);
            notifyUpdateProgress();
        }
    }

    /**
     * 运行函数业务逻辑
     */
    abstract void runHandler() throws Exception;


    @Setter
    private Callback<P> notifyUpdateProgressCallback;

    /**
     * 通知更新进度
     */
    public void notifyUpdateProgress() {
        if (notifyUpdateProgressCallback != null) notifyUpdateProgressCallback.invoke(progress);
        notificationService.notifyUpdateProgress(taskId);
        NotificationService bean = SpringUtil.getBean(NotificationService.class);
        debounce.run(() -> bean.notifyUpdateProgress(taskId));
    }

    /**
     * 检查中断遇到中断时的回调函数
     */
    public interface CheckInterruptedCallback {
        /**
         * 中断时触发
         */
        void onInterrupted();
    }

    /**
     * 检查服务是否收到中断信号
     * 如果收到中断信号，抛出异常
     * 子类可以在原子节点调用该方法，以捕获中断信号并退出
     */
    public void checkInterrupted() {
        checkInterrupted("未知异常");
    }

    /**
     * 检查服务是否收到中断信号
     * 如果收到中断信号，抛出异常
     * 子类可以在原子节点调用该方法，以捕获中断信号并退出
     *
     * @param format 错误原因（格式化字符串）
     * @param args   参数列表
     */
    public void checkInterrupted(String format, Object... args) {
        if (isInterrupted()) {
            throw new ServiceException("[taskId {}]" + format, taskId, args);
        }
    }

    /**
     * 中断任务并等待任务结束
     * 1. 中断任务方式为中断信号量；
     * 2. 子类在执行任务的时候可以在原子节点调用 {@link #checkInterrupted()} 方法
     */
    public void interruptWaitStop() {
        if (!isRunning()) return;
        interrupt();
        try {
            this.join();
        } catch (InterruptedException e) {
            throw new ServiceException("服务中断异常 join ！[task id] {}", taskId);
        }
    }

    /**
     * 是否完成
     *
     * @return boolean
     */
    public boolean isFinish() {
        return isSuccess() || isFail();
    }

    /**
     * 是否成功结束
     *
     * @return boolean
     */
    public boolean isSuccess() {
        return Status.FINISH.equals(status);
    }

    /**
     * 是否失败
     *
     * @return boolean
     */
    public boolean isFail() {
        return Status.FAIL.equals(status);
    }

    /**
     * 是否运行中
     *
     * @return boolean
     */
    public boolean isRunning() {
        return Status.RUNNING.equals(status);
    }


    /**
     * 任务进度
     */
    @Getter
    @Setter
    public static class Progress {

        @JsonIgnore
        private V3Task<?, ?> task;

        public String getStatus() {
            return task.getStatus();
        }

        public boolean isFinish() {
            return task.isFinish();
        }

        public boolean isSuccess() {
            return task.isSuccess();
        }

        public boolean isFail() {
            return task.isFail();
        }

        public boolean isRunning() {
            return task.isRunning();
        }

        public String errorMessage() {
            return task.getErrorMessage();
        }
    }

    /**
     * 任务状态
     */
    public static class Status {
        // 等待运行
        public static String WAITING = "waiting";
        // 运行中
        public static String RUNNING = "running";
        // 完成
        public static String FINISH = "finish";
        // 错误
        public static String FAIL = "fail";
    }

}
